package com.jeeplus.modules.common.excel.core;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelCommonException;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.jeeplus.common.utils.StringUtil;
import com.jeeplus.modules.common.constant.CommonConstants;
import com.jeeplus.modules.common.excel.common.anno.abs.ExcelValidator;
import com.jeeplus.modules.common.excel.common.validator.abs.AbstractValidator;
import com.jeeplus.modules.common.excel.common.validator.impl.unique.UniqueDataValidator;
import com.jeeplus.modules.common.excel.component.operator.*;
import com.jeeplus.modules.common.excel.component.setter.ErrorMsgSetter;
import com.jeeplus.modules.common.excel.component.strategies.RemoveEmptyStrategy;
import com.jeeplus.modules.common.excel.common.validator.abs.AbstractCustomValidator;
import com.jeeplus.modules.common.excel.common.validator.chain.AbstractValidatorChain;
import com.jeeplus.modules.common.excel.common.validator.chain.impl.ValidatorChain;
import com.jeeplus.modules.common.utils.*;
import com.jeeplus.modules.da.utils.BaseRedisService;
import org.activiti.bpmn.model.Interface;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.security.cert.TrustAnchor;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * excel工具组件
 * @author wang.yishu
 */
public class Excel<T> {

    //数据校验链
    private AbstractValidatorChain chain = new ValidatorChain();

    private ErrorMsgSetter<T> errorMsgSetter;

    private List<T> errorList = null;

    private DataOperator<T> dataOperator;

    private Map<String, Integer> importProcessMap;

    private BeforeRead beforeRead;

    private InRead inRead;

    private AfterRead<T> afterRead;

    private FinalRead finalRead;

    private String importProcessErrorName;//导入错误进程名称

    private boolean needCheckHeader;

    public boolean isNeedCheckHeader() {
        return needCheckHeader;
    }

    public void setNeedCheckHeader(boolean needCheckHeader) {
        this.needCheckHeader = needCheckHeader;
    }

    public Excel() {
        /*this.baseRedisService = SpringBeanUtils.getBean(BaseRedisService.class);
        this.importLogService = SpringBeanUtils.getBean(ImportLogService.class);*/
    }

    public void setOperator(InterfaceOperator<T> operator) {
        this.setBeforeRead(operator.beforeRead());//导入之前的操作
        this.setInRead(operator.inRead());//导入同时的操作
        this.setAfterRead(operator.afterRead());//导入后操作
        this.setFinalRead(operator.finalRead());//最终操作
    }

    public void setValidatorChain(Class<T> tClass) {
        Field[] declaredFields = tClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            Annotation[] declaredAnnotations = declaredField.getDeclaredAnnotations();
            for (Annotation declaredAnnotation : declaredAnnotations) {
                Class<? extends Annotation> aClass = declaredAnnotation.annotationType();
                ExcelValidator excelValidator = aClass.getDeclaredAnnotation(ExcelValidator.class);
                if (excelValidator != null) {
                    Class<? extends AbstractValidator> validator = excelValidator.validator();
                    if (validator != null) {
                        try {
                            AbstractValidator abstractValidator = validator.newInstance();
                            this.chain.addNode(abstractValidator);
                        } catch (InstantiationException | IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * 读取excel数据，并返回读取好的正确数据
     * @param file
     * @param tClass
     * @param validators
     * @return void
     */
    public void read(MultipartFile file, Class<T> tClass, AbstractCustomValidator... validators) throws Exception {
        List<T> dataList = new ArrayList<>();
        importProcessMap = new HashMap<>();
        this.setValidatorChain(tClass);
        //添加唯一性数据校验组件
        chain.addNode(new UniqueDataValidator());
        //添加自定义校验器
        if (validators != null && validators.length > 0) {
            for (int i = 0; i < validators.length; i++) {
                chain.addNode(validators[i]);
            }
        }
        try {
            InputStream inputStream = file.getInputStream();
            if (!this.needCheckHeader) {
                EasyExcel.read(inputStream, tClass, new PageReadListener<T>(dataList::addAll)).sheet().doRead();
            } else {
                try {
                    EasyExcel.read(inputStream, tClass, new ReadHeadListener<T>(dataList::addAll)).sheet().doRead();
                } catch (Exception e) {
                    throw new RuntimeException("导入文件发生错误，可能是文件与模板不一致！");
                }
                Map<String,String> headMap = (Map<String, String>) dataList.get(0);
                boolean checkHeader = this.checkHeader(tClass, headMap);
                if (!checkHeader) {
                    throw new RuntimeException("上传的文件错误，不符合导入模板！");
                }
                //删除表头
                dataList.remove(0);
            }
            List<T> list = this.removeEmptyData(dataList);
            if (list == null || list.size() == 0) {
                throw new RuntimeException("上传的文件中无可用数据！");
            }
            this.setTotalCount(list.size());//设置总数据条数
            this.setImportCount(0);
            if(totalCount.get() > CommonConstants.MAXROWCOUNT){
                throw new RuntimeException(CommonConstants.MAXROWERRMESSAGE);
            }
            importProcessMap.put("total", totalCount.get());
            importProcessMap.put("done",importCount.get());
            if (this.beforeRead != null) {
                this.beforeRead.beforeRead(this.getImportProcessKey(), importProcessMap);
            }
            //分批次导入
            this.chain.getNecessaryValidateData().put(UniqueDataValidator.key, (List<Object>) dataList);
            errorList = new ArrayList<>();
            //执行校验，设置错误数据集，并返回通过数据集
            List<T> passDataList = this.validateData(dataList, errorList);
            importCount.set(errorList.size());
            this.operatorData(passDataList);
            totalFailed = errorList == null ? 0 : errorList.size();
            totalSuccess = totalCount.get() - totalFailed;
            if (this.afterRead != null) {
                this.afterRead.afterRead(errorList, totalCount.get(), totalSuccess, totalFailed, this.getImportProcessErrorName(),tClass);
            }
            this.importProcessMap.put("success",totalSuccess);
            this.importProcessMap.put("failed",totalFailed);
        } finally {
            this.finalRead.finalRead(this.getImportProcessKey());
        }
    }

    public Map<String, Integer> getImportProcessMap() {
        return importProcessMap;
    }

    public void setImportProcessMap(Map<String, Integer> importProcessMap) {
        this.importProcessMap = importProcessMap;
    }

    private boolean checkHeader(Class<T> tClass, Map<String, String> headMap) {
        Field[] declaredFields = tClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            String name = declaredField.getName();
            ExcelProperty excelProperty = declaredField.getDeclaredAnnotation(ExcelProperty.class);
            if (excelProperty != null && !"errorMsg".equals(name)) {
                String title = headMap.get(name);
                if (title == null) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * @author wys
     * @description 写出数据
     * @date 22:43 2022/7/6
     * @param data  数据集
     * @return void
     */
    public void write(List<T> data) {
    }

    private int operatorData(List<T> passDataList) {
        int count = 0;
        for (T t : passDataList) {
            this.importCount.getAndIncrement();//自增
            this.importProcessMap.put("total",totalCount.get());
            this.importProcessMap.put("done", importCount.get());
            if(this.inRead != null) {
                this.inRead.inRead(this.getImportProcessKey(), this.importProcessMap);
            }
            if (this.dataOperator != null) {
                this.dataOperator.operator(t);
            }
            count++;
        }
        return count;
    }

    public String getImportProcessErrorName() {
        return importProcessErrorName;
    }

    public void setImportProcessErrorName(String importProcessErrorName) {
        this.importProcessErrorName = importProcessErrorName;
    }

    public FinalRead getFinalRead() {
        return finalRead;
    }

    public void setFinalRead(FinalRead finalRead) {
        this.finalRead = finalRead;
    }

    /**
     * @author wys
     * @description 执行数据校验
     * @date 21:59 2022/7/6
     * @param data  原始数据集
     * @param errorList 错误数据集
     * @return  正确数据集
     */
    private List<T> validateData(List<T> data, List<T> errorList) {
        List<T> rtnList = new ArrayList<>();
        List<Object> importData = new ArrayList<>();
        for (T datum : data) {
            //使用校验链执行数据校验
            String errorMsg = this.chain.doValidate(datum);
            List<AbstractCustomValidator> customValidatorList = this.chain.getCustomValidatorList();
            if (customValidatorList != null && customValidatorList.size() > 0) {
                StringBuilder customErrMsg = new StringBuilder("");
                for (AbstractCustomValidator abstractCustomValidator : customValidatorList) {
                    String customValidate = abstractCustomValidator.customValidate(datum);
                    if (StringUtil.isNotBlank(customValidate)) {
                        customErrMsg.append(customValidate);
                    }
                }
                errorMsg = errorMsg + customErrMsg.toString();
            }
            if (StringUtil.isNotBlank(errorMsg)) {
                this.errorMsgSetter.setErrorMsg(errorMsg, datum);
                errorList.add(datum);
            } else {
                rtnList.add(datum);
            }
        }
        return rtnList;
    }

    /**
     * 删除空数据
     * @param list  数据集
     * @return
     */
    private List<T> removeEmptyData(List<T> list) {
        if (this.strategy == null) {
            List<T> rtnList = new ArrayList<>();
            for (int i = 0; i < list.size(); i++) {
                T t = list.get(i);
                boolean isEmpty = true;
                Class<?> aClass = t.getClass();
                Field[] declaredFields = aClass.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    declaredField.setAccessible(true);
                    try {
                        Object value = declaredField.get(t);
                        if (value != null) {
                            isEmpty = false;
                            break;
                        }
                    } catch (IllegalAccessException e) {
                    }
                }
                if (!isEmpty) {
                    rtnList.add(t);
                }
            }
            return rtnList;
        } else {
            return this.strategy.removeEmptyData(list);
        }
    }

    private RemoveEmptyStrategy<T> strategy;//清除策略

    public AbstractValidatorChain getChain() {
        return chain;
    }

    public void setChain(AbstractValidatorChain chain) {
        this.chain = chain;
    }

    public RemoveEmptyStrategy<T> getStrategy() {
        return strategy;
    }

    public ErrorMsgSetter<T> getErrorMsgSetter() {
        return errorMsgSetter;
    }

    public void setErrorMsgSetter(ErrorMsgSetter<T> errorMsgSetter) {
        this.errorMsgSetter = errorMsgSetter;
    }

    private int totalFailed; //总失败数
    private int totalSuccess;//总成功数
    AtomicInteger totalCount = new AtomicInteger(0);//导入总数
    AtomicInteger importCount = new AtomicInteger(0);//已导入数
    private String importProcessKey;//Redis导入进程key

    public String getImportProcessKey() {
        return importProcessKey;
    }

    public void setImportProcessKey(String importProcessKey) {
        this.importProcessKey = importProcessKey;
    }

    public int getTotalFailed() {
        return totalFailed;
    }

    public void setTotalFailed(int totalFailed) {
        this.totalFailed = totalFailed;
    }

    public int getTotalSuccess() {
        return totalSuccess;
    }

    public void setTotalSuccess(int totalSuccess) {
        this.totalSuccess = totalSuccess;
    }

    public AtomicInteger getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(int totalCount) {
        this.totalCount.set(totalCount);
    }

    public AtomicInteger getImportCount() {
        return importCount;
    }

    public void setImportCount(int importCount) {
        this.importCount.set(importCount);
    }

    public void setStrategy(RemoveEmptyStrategy<T> strategy) {
        this.strategy = strategy;
    }

    public DataOperator<T> getDataOperator() {
        return dataOperator;
    }

    public void setDataOperator(DataOperator<T> dataOperator) {
        this.dataOperator = dataOperator;
    }

    public BeforeRead getBeforeRead() {
        return beforeRead;
    }

    public void setBeforeRead(BeforeRead beforeRead) {
        this.beforeRead = beforeRead;
    }

    public InRead getInRead() {
        return inRead;
    }

    public void setInRead(InRead inRead) {
        this.inRead = inRead;
    }

    public AfterRead<T> getAfterRead() {
        return afterRead;
    }

    public void setAfterRead(AfterRead<T> afterRead) {
        this.afterRead = afterRead;
    }

    public static <X> void downloadTemplate(Class<X> xClass, String fileName, HttpServletResponse response) throws IOException {
        response.addHeader(CommonConstants.CONTENT_DISPOSITION, CommonConstants.ATTACHMENT + "\"" + URLEncoder.encode(fileName, "UTF-8") + "\"");
        response.addHeader(CommonConstants.CONTENT_TYPE, CommonConstants.SHEET);
        Field[] declaredFields = xClass.getDeclaredFields();
        List<String> fieldNames = new ArrayList<>();
        for (int i = 0; i < declaredFields.length; i++) {
            ExcelProperty declaredAnnotation = declaredFields[i].getDeclaredAnnotation(ExcelProperty.class);
            if (declaredAnnotation != null) {
                if (declaredFields[i].getName().equals("errorMsg")) {
                    continue;
                }
                fieldNames.add(declaredFields[i].getName());
            }
        }
        Set<String> exportColumns = new HashSet<String>(fieldNames);
        ServletOutputStream outputStream = response.getOutputStream();
        List<X> list = new ArrayList<>();
        ExcelWriterBuilder excelBuilder = EasyExcel.write(outputStream, xClass)
                .needHead(true)
                .registerWriteHandler(ExcelUtil.getStyleStrategy());
        excelBuilder.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
        excelBuilder.includeColumnFiledNames(exportColumns);
        excelBuilder.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
        excelBuilder.registerWriteHandler(new XgSheetStyleStrategy());
        excelBuilder.sheet("sheet").doWrite(list);
    }
}
